
#include "rgb.h"
#include "tim.h" // ????????????,?? tim.h
#include <string.h> // ??memset

// LED?????,??????????
RGB_Color_t LED_Buffer[WS2812B_NUM_LEDS];

// DMA?????
// ??LED??24bit,??bit?DMA????1???(CCR?)
// ???(???)????50us,???????3?LED?????(~84???)
#define BITS_PER_LED  24
#define RESET_CODE_LENGTH (50 * 2) // ????,??????????
#define DMA_BUFFER_SIZE  ( (WS2812B_NUM_LEDS * BITS_PER_LED) + RESET_CODE_LENGTH )

static uint16_t DMA_Buffer[DMA_BUFFER_SIZE] = {0};

// 0??1??CCR?,??????????????!
// ??????????168MHz, APB2?????84MHz, PWM??800kHz (ARR=105)
#define CODE_0   (67)  // 0.36us  ~ 30/84MHz
#define CODE_1   (134)  // 0.77us  ~ 65/84MHz
// ????0(???)

/**
  * @brief  ?24????????DMA?????
  * @param  color: ??? (GRB)
  * @param  position: ?DMA?????????
  * @retval None
  */
static void WS2812B_ColorToBuffer(uint32_t color, uint16_t position) {
    uint32_t mask = 0x800000; // GRB??,????(G7)????
    for (uint8_t i = 0; i < 24; i++) {
        DMA_Buffer[position + i] = (color & mask) ? CODE_1 : CODE_0;
        mask >>= 1;
    }
}

/**
  * @brief  ???WS2812B??
  * @retval None
  */
void WS2812B_Init(void) {
    // ??DMA??,????
    HAL_TIM_PWM_Stop_DMA(WS2812B_TIMER_HANDLE, WS2812B_TIMER_CHANNEL);

    // ???????
    WS2812B_ClearAll();

    // ???DMA???,?????0(???)
    for (uint16_t i = 0; i < DMA_BUFFER_SIZE; i++) {
        DMA_Buffer[i] = 0;
    }

    // ????,???????????DMA???
    WS2812B_Update();
}

/**
  * @brief  ????LED??? (RGB)
  * @param  led_idx: LED?? (0 ~ WS2812B_NUM_LEDS-1)
  * @param  r: ??? (0-255)
  * @param  g: ??? (0-255)
  * @param  b: ??? (0-255)
  * @retval None
  */
void WS2812B_SetLEDColor(uint16_t led_idx, uint8_t r, uint8_t g, uint8_t b) {
    if (led_idx < WS2812B_NUM_LEDS) {
        LED_Buffer[led_idx].g = g;
        LED_Buffer[led_idx].r = r;
        LED_Buffer[led_idx].b = b;
    }
}

/**
  * @brief  ?????????LED???
  * @param  led_idx: LED??
  * @param  color: RGB?????
  * @retval None
  */
void WS2812B_SetLEDColorRGB(uint16_t led_idx, RGB_Color_t color) {
    if (led_idx < WS2812B_NUM_LEDS) {
        LED_Buffer[led_idx] = color;
    }
}

/**
  * @brief  ????LED??(????/??)
  * @retval None
  */
void WS2812B_ClearAll(void) {
    for (uint16_t i = 0; i < WS2812B_NUM_LEDS; i++) {
        LED_Buffer[i].r = 0;
        LED_Buffer[i].g = 0;
        LED_Buffer[i].b = 0;
    }
}

/**
  * @brief  ??LED??,????????
  * @retval None
  */
void WS2812B_Update(void) {
    // 1. ?????????????DMA???
    for (uint16_t i = 0; i < WS2812B_NUM_LEDS; i++) {
        // ???GRB???24???
        uint32_t grb_color = ((uint32_t)LED_Buffer[i].g << 16) |
                             ((uint32_t)LED_Buffer[i].r << 8) |
                              (uint32_t)LED_Buffer[i].b;

        WS2812B_ColorToBuffer(grb_color, i * 24);
    }

    // 2. DMA???????????0(???),??????

    // 3. ?????DMA??
    HAL_TIM_PWM_Stop_DMA(WS2812B_TIMER_HANDLE, WS2812B_TIMER_CHANNEL);

    // 4. ??DMA???????
    // HAL???:?DMA??????????TIMx->CCRx???
    HAL_TIM_PWM_Start_DMA(WS2812B_TIMER_HANDLE, WS2812B_TIMER_CHANNEL, (uint32_t *)DMA_Buffer, DMA_BUFFER_SIZE);
}

/**
  * @brief  ???????????
  * @retval None
  */
void WS2812B_TestPattern(void) {
    for (uint16_t i = 0; i < WS2812B_NUM_LEDS; i++) {
        // ?????????
        if (i % 3 == 0) {
            WS2812B_SetLEDColor(i, 255, 0, 0);     // ?
        } else if (i % 3 == 1) {
            WS2812B_SetLEDColor(i, 0, 255, 0);     // ?
        } else {
            WS2812B_SetLEDColor(i, 0, 0, 255);     // ?
        }
    }
    WS2812B_Update();
}
